痾其實我也不知道會不會完賽,升上研究所之後變得好忙...
在寫套件之前,我們要知道自己要把什麼東西「造出來」、用什麼工具「把它運到瀏覽器」,以及前端如何「優雅地使用它」。所以我不會先講很多 Rust 語法;先理解輸出形態與 toolchain,之後所有選擇都會明朗而省時。
Rust 要跑進瀏覽器,最終必須變成 WebAssembly,並透過一層 JavaScript glue 被前端使用。這裡有三個面向需要去釐清:
產物長相
你的 crate 會輸出成一個 .wasm 檔與相對應的 JS shim(由 wasm-bindgen 生成),還可能附帶 .d.ts 型別定義。
如何被前端載入
現代前端以 ESM 為主。你需要讓 bundler 或 runtime 知道要如何 import 你的封裝、去哪裡找到 .wasm、要不要在 Web Worker 跑。
封裝與發佈
面向 npm 的套件,要處理 package.json 的 exports map、型別檔、瀏覽器與 Node 的差異入口,以及效能/體積的平衡。
把這三件事想清楚,後面的 lib 與 cdylib、workspace、wasm-pack、Vite/Next 只是各自站在不同關卡的小幫手。
Rust Code → 編成 WebAssembly → 由 wasm-bindgen 生成 JS/TS API → 交給前端 bundler 或 runtime 透過 ESM 載入與使用。
Rust crate (lib)
└─ cargo build --target wasm32-unknown-unknown
└─ .wasm 產物
└─ wasm-bindgen/wasm-pack 生成 JS glue + d.ts
└─ 前端 (Vite/Next) 以 ESM import
└─ 瀏覽器 / Web Worker 執行
檢查命令
rustup --version
cargo --version
rustup target list --installed
wasm-pack --version
node -v
pnpm -v
建議目標平台安裝
rustup target add wasm32-unknown-unknow
知道到這裡即可下面是一些更詳細的講解(-0-)
lib 與 cdylib 的差別:
這邊我會建議用 workspace 分成 core 與 wasm 兩個 crate:core(lib)純邏輯、零平台耦合、可複用、可測試。然後 wasm(cdylib)專責與 JS 溝通(如 wasm-bindgen 標註、型別包裝、錯誤轉譯)。這樣做可以把演算法和平台邊界解耦,架構會更清楚明瞭。
wasm-bindgen 跟 wasm-pack 分別是什麼:
常見工作流程
前端體驗的關鍵在 package.json 的設計:
為什麼重要?如果沒有 exports map,使用者的 bundler 可能找不到真正入口或把內部檔案誤當公開 API;沒有 d.ts,TS user experience 會大打折扣;而沒有明確 ESM,會在 Node 與瀏覽器間出現相容問題。
當你的運算很重、會阻塞 UI 主緒時,就把 wasm 跑到 Worker,而我們要做的套件運算都偏大所以會用到 worker。
所以是這樣安排的:
[crate: core (lib)] [crate: wasm (cdylib + wasm-bindgen)]
│ │
└── 演算法/邏輯 ────► │ API、錯誤轉譯
│
cargo build --target wasm32-unknown-unknown
│
wasm-bindgen / wasm-pack
│
生成 pkg/: index.js + package.json + .wasm + .d.ts
│
前端 (Vite/Next) 以 ESM import
│
瀏覽器主緒 或 Web Worker 實際執行與渲染
今天釐清了一些名詞與基本概念,名詞如下:crate、workspace、lib/cdylib、wasm-bindgen、wasm-pack、ESM、Web Worker、exports map、d.ts 。以下是三個小問題,希望讓大家檢驗有沒有在文章內學到東西:
lib 與 cdylib 的差別
lib 是一般函式庫,偏邏輯內聚;cdylib 用於跨語言邊界的產物,搭 wasm-bindgen 才能讓 JS 直接用。實務上常採 lib + cdylib 分層:lib 放核心邏輯、cdylib 專注 JS 介面。
workspace 的用途
把多個 crate 放在一個倉庫中共享版本、鎖檔與工具鏈:可把核心邏輯與平台邊界切開;之後你想同時輸出「瀏覽器版 wasm」和「Node 原生擴充」也更好管理。
exports map 為何重要
它決定「使用者 import 你的套件時」實際拿到哪個檔案、在哪個環境下走哪個入口,並避免外部依賴你的內部檔案路徑。這是維持 API 穩定與 DX 的關鍵。
在規劃的時候覺得還好,真的寫出來快瘋掉,好好笑不能確保之後的內容還會這麼多真的是在搞死自己,唸的人也很痛苦我很抱歉。